package com.example.design.filter.context;


import com.example.design.DateUtils;
import lombok.Getter;
import org.apache.commons.collections.CollectionUtils;

import java.time.LocalDate;
import java.util.*;

/**
 * 排班计划上下文
 */
@Getter
public abstract class ScheduleContext {

    /**
     * 排班的年月信息
     */
    protected final Integer year;
    protected final Integer month;
    /**
     * 排班天数(决定了排班的开始和结束时间)
     */
    private transient Long scheduleLength;
    /**
     * 真实的排班开始&结束时间，存在跨月
     */
    private LocalDate startDate;
    private LocalDate endDate;

    /**
     * 排班小组id
     */
    private final Long teamId;
    /**
     * 排班小组下的店铺
     */
    private final List<Long> shopIds;
    /**
     * 待排班员工 - 初始数据,不可修改
     *
     * @see com.leqee.csr.qc.service.business.act.schedule.support.context.ScheduleUser
     */
    private final List<ScheduleUser> users;
    /**
     * 当前排班日期
     */
    protected LocalDate scheduleDate;
    /**
     * 每日所需班次数据 <Date(yyyyMMdd), shiftNum>
     */
    protected Map<String, Integer> shiftMap;
    /**
     * 每日所需班次数量
     */
    protected Integer shiftNum;
    /**
     * 优先排班员工 - 每日清空
     *
     * @see com.leqee.csr.qc.service.business.act.schedule.support.context.ScheduleContext#nextDay()
     * @see com.leqee.csr.qc.service.business.act.schedule.support.context.ScheduleUser
     */
    protected List<ScheduleUser> priorityUsers = new ArrayList<>();

    /**
     * 当日需要被排班的员工 - 每日重新初始化
     *
     * @see com.leqee.csr.qc.service.business.act.schedule.support.context.ScheduleContext#nextDay()
     * @see com.leqee.csr.qc.service.business.act.schedule.support.context.ScheduleUser
     */
    protected List<ScheduleUser> scheduleUsers;

    /**
     * 休息&请假 <Date(yyyyMMdd), List<ScheduleUser.class>>
     *
     * @see com.leqee.csr.qc.service.business.act.schedule.support.context.ScheduleUser
     */
    protected Map<String, Set<ScheduleUser>> restMap = new HashMap<>(8);

    public ScheduleContext(Long teamId, List<Long> shopIds, LocalDate scheduleDate,
                           Map<String, Integer> shiftMap, List<ScheduleUser> users) {
        this.year = scheduleDate.getYear();
        this.month = scheduleDate.getMonthValue();

        this.teamId = teamId;
        this.shopIds = shopIds;
        this.scheduleDate = scheduleDate;

        this.shiftMap = shiftMap;
        this.users = users;
        this.scheduleUsers = new ArrayList<>(users);

        this.getScheduleLength();
    }

    /**
     * 排班班次
     */
    public abstract List<String> getScheduleShifts();

    /**
     * 排班时间长度
     */
    public long getScheduleLength() {
        if (scheduleLength != null) {
            return scheduleLength;
        }

        LocalDate monthStart = LocalDate.of(year, month, 1);
        endDate = monthStart.plusDays(monthStart.lengthOfMonth() - 1);
        // 排班时间是月初 并且不是周一 并且周一在当前时间之后，需要去更新上个月的排班数据
        if (scheduleDate.getDayOfMonth() == 1 &&
                scheduleDate.getDayOfWeek().getValue() != 1 &&
                LocalDate.now().isBefore(scheduleDate)) {
            scheduleDate = scheduleDate.plusDays(-1L * scheduleDate.getDayOfWeek().getValue() + 1);
        }
        startDate = scheduleDate;
        return scheduleLength = endDate.toEpochDay() - startDate.toEpochDay();
    }

    public void setShiftNum(Integer shiftNum) {
        this.shiftNum = shiftNum;
    }

    /**
     * 用户按照uid排序 单月正序 双月倒序
     */
    public void sort() {
        int month = scheduleDate.getMonthValue();
        if (month % 2 > 0) {
            if (CollectionUtils.isNotEmpty(priorityUsers)) {
                priorityUsers.sort((u1, u2) -> (int) (u1.getUserId() - u2.getUserId()));
            }
            if (CollectionUtils.isNotEmpty(scheduleUsers)) {
                scheduleUsers.sort((u1, u2) -> (int) (u1.getUserId() - u2.getUserId()));
            }
            return;
        }
        if (CollectionUtils.isNotEmpty(priorityUsers)) {
            priorityUsers.sort((u1, u2) -> (int) (u2.getUserId() - u1.getUserId()));
        }
        if (CollectionUtils.isNotEmpty(scheduleUsers)) {
            scheduleUsers.sort((u1, u2) -> (int) (u2.getUserId() - u1.getUserId()));
        }
    }

    /**
     * 切换上下文到下一天
     */
    public void nextDay() {
        scheduleDate = scheduleDate.plusDays(1);
        priorityUsers.clear();
        scheduleUsers = new ArrayList<>(users);
        if (scheduleDate.getDayOfWeek().getValue() == 1) {
            this.initLastSundayShift();
        }
    }

    /**
     * 切换上下文周时初始化用户的上周末班次信息
     */
    private void initLastSundayShift() {
        LocalDate lastSunday = scheduleDate.plusDays(-1);
        String sundayKey = lastSunday.format(DateUtils.FORMAT_YYYYMMDD);
        users.parallelStream().forEach(
                user -> user.setLastSundayShift(user.getShiftMap().get(sundayKey))
        );
    }

    public void addRestUsers(List<ScheduleUser> users) {
        String dateKey = scheduleDate.format(DateUtils.FORMAT_YYYYMMDD);
        for (ScheduleUser user : users) {
            user.getShiftMap().put(dateKey, user.getScheduleType().getRestShift());
        }
        Set<ScheduleUser> restUsers = restMap.computeIfAbsent(
                dateKey, k -> new HashSet<>()
        );
        restUsers.addAll(users);
        scheduleUsers.removeAll(users);
    }

    public void addRestUser(ScheduleUser user) {
        String dateKey = scheduleDate.format(DateUtils.FORMAT_YYYYMMDD);
        user.getShiftMap().put(dateKey, user.getScheduleType().getRestShift());
        Set<ScheduleUser> restUsers = restMap.computeIfAbsent(
                dateKey, k -> new HashSet<>()
        );
        restUsers.add(user);
        scheduleUsers.remove(user);
    }

    public void addPriorityUser(List<ScheduleUser> users) {
        priorityUsers.clear();
        priorityUsers.addAll(users);
    }
}
